#include <atomic>
#include <chrono>
#include <cstring>
#include <thread>
#include <sstream>

#include <include/core/SkTextBlob.h>
#include <include/core/SkBitmap.h>
#include <include/core/SkCanvas.h>
#include <include/core/SkFont.h>
#include <include/core/SkTypeface.h>

#include <SDL2/SDL.h>

using namespace std::chrono_literals;

class Main {
    SDL_Window *window_ = nullptr;
    SDL_Renderer *renderer_ = nullptr;
    SDL_Surface *surface_ = nullptr;
    SDL_Texture *texture_ = nullptr;
    SDL_Rect rect_ = {};

    std::unique_ptr<std::thread> uiThread_ = nullptr;
    std::atomic<bool> uiThreadRunning_ = true;
    int fps = 0;
    std::string fpsString = "";
    int fpsCount = 0;
    std::chrono::time_point<std::chrono::steady_clock> fpsNextTime = std::chrono::steady_clock::now();

    SkBitmap bitmap_ = {};

public:
    Main(int winWidth, int winHeight)
    {
        SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
        constexpr auto centered = SDL_WINDOWPOS_CENTERED;
        constexpr auto flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN;
        window_ = SDL_CreateWindow("Hello Skia", centered, centered, winWidth, winHeight, flags);
        renderer_ = SDL_CreateRenderer(window_, -1, SDL_RENDERER_ACCELERATED);
        SDL_RenderClear(renderer_);
        surface_ = SDL_GetWindowSurface(window_);
        rect_ = { 0, 0, surface_->w, surface_->h };

        uiThread_ = std::make_unique<std::thread>(std::bind(&Main::UIThreadMain, this));
    }

    ~Main()
    {
        uiThread_->join();
        if (surface_) {
            SDL_FreeSurface(surface_);
        }

        if (texture_) {
            SDL_DestroyTexture(texture_);
        }

        if (renderer_) {
            SDL_DestroyRenderer(renderer_);
        }

        if (window_) {
            SDL_DestroyWindow(window_);
        }

        SDL_Quit();
    }

    void Run()
    {
        while (uiThreadRunning_) {
            SDL_Event e;
            while (SDL_PollEvent(&e)) {
                if (e.type == SDL_QUIT) {
                    uiThreadRunning_ = false;
                }
            }
        }
    }

    void UIThreadMain()
    {
        while (uiThreadRunning_) {
            CalcFPS();
            if (surface_) {
                SDL_FreeSurface(surface_);
                surface_ = nullptr;
            }

            if (texture_) {
                SDL_DestroyTexture(texture_);
                texture_ = nullptr;
            }

            bitmap_ = Draw();
            constexpr uint32_t rmask = 0x00ff0000;
            constexpr uint32_t gmask = 0x0000ff00;
            constexpr uint32_t bmask = 0x000000ff;
            constexpr uint32_t amask = 0xff000000;
            surface_ = SDL_CreateRGBSurfaceFrom(bitmap_.getPixels(),
                rect_.w, rect_.h, 32, rect_.w * 4, rmask, gmask, bmask, amask);
            texture_ = SDL_CreateTextureFromSurface(renderer_, surface_);
            SDL_RenderCopy(renderer_, texture_, NULL, &rect_);
            SDL_RenderPresent(renderer_);
            std::this_thread::sleep_for(16ms);
        }
    }

    void CalcFPS()
    {
        auto now = std::chrono::steady_clock::now();
        if (now > fpsNextTime) {
            fpsNextTime = now + 1s;
            fps = fpsCount;
            fpsCount = 0;
        } else {
            fpsCount++;
        }
    }

    SkBitmap Draw()
    {
        auto w = rect_.w;
        auto h = rect_.h;
        SkBitmap bitmap;
        bitmap.setInfo(SkImageInfo::Make(w, h, kBGRA_8888_SkColorType, kOpaque_SkAlphaType));
        bitmap.allocPixels();
        SkCanvas canvas(bitmap);
        SkPaint paint;
        canvas.clear(SK_ColorWHITE);
        paint.setAntiAlias(true);
        paint.setARGB(255, 255, 0, 0);
        canvas.drawCircle(80, 80, 40, paint);
        canvas.drawLine(0, 260, w, 280, paint);
        canvas.drawLine(0, 280, w, 280, paint);
        canvas.drawLine(0, 300, w, 280, paint);

        SkFont font;
        font.setSize(60);
        canvas.drawString("Hello Skia Again", 300, 150, font, paint);

        font.setSize(20);
        std::stringstream ss;
        ss << "FPS: " << fps;
        fpsString = ss.str();
        auto textBlob = SkTextBlob::MakeFromString(fpsString.c_str(), font);
        canvas.drawTextBlob(textBlob, 10, 20, paint);
        return bitmap;
    }
};

int main()
{
    Main main(800, 640);
    main.Run();
    return 0;
}
